/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.tencent.tendon.convert.json;

import java.util.*;
import java.lang.reflect.*;

/**
 *
 * @author nbzhang
 */
public class JsonCollectionDecoder<T> extends JsonDecoder<Collection<T>> {

    private final JsonDecodable<T> decoder;

    JsonCollectionDecoder(final Type type) {
        super(parseRawType(type));
        if (type instanceof ParameterizedType) {
            this.decoder = JsonLoader.loadDecodable(((ParameterizedType) type).getActualTypeArguments()[0]);
        } else {
            throw new JsonException("not support the type (" + type + ")");
        }
        this.simpled = decoder.isSimpled();
    }

    private JsonCollectionDecoder(final Class<Collection<T>> type, final JsonDecodable<T> decoder) {
        super(type);
        this.decoder = decoder;
        this.simpled = decoder.isSimpled();
    }

    public static <T> JsonCollectionDecoder<T> create(final Class<Collection<T>> type, final JsonDecodable<T> decoder) {
        return new JsonCollectionDecoder(type, decoder);
    }

    @Override
    @SuppressWarnings("unchecked")
    public Collection<T> convertFrom(final JsonReader in) {
        if (!in.checkArray()) { //循环引用
            return (Collection<T>) JsonReferDecoder.getInstance().convertFrom(in);
        }
        final Collection<T> result = this.creator.create();
        if (!simpled) in.offer(result);
        final JsonDecodable<T> localdecoder = this.decoder;
        while (in.hasNext()) {
            result.add(localdecoder.convertFrom(in));
        }
        if (!simpled) in.poll();
        return result;
    }

    @Override
    public String toString() {
        return this.getClass().getSimpleName() + "{decoder : " + decoder + "}";
    }

    private static Class parseRawType(final Type type) {
        if (type instanceof ParameterizedType) {
            return parseRawType(((ParameterizedType) type).getRawType());
        } else if (type instanceof Class && Collection.class.isAssignableFrom((Class) type)) {
            Class clazz = (Class) type;
            if (clazz.isAssignableFrom(ArrayList.class)) return ArrayList.class;
            if (clazz.isAssignableFrom(HashSet.class)) return HashSet.class;
            return clazz;
        } else {
            throw new JsonException("not support the type (" + type + ")");
        }
    }
}